package top.cardone.context;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import top.cardone.core.util.action.Action0;
import top.cardone.core.util.action.Action1;
import top.cardone.core.util.func.Func0;
import top.cardone.core.util.func.Func1;

/**
 * 上下文支持
 *
 * @author yao hai tao
 */
@lombok.extern.log4j.Log4j2
public class ApplicationContextHolder implements ApplicationContextAware {
	/**
	 * Spring ApplicationContext
	 */
	@lombok.Getter
	private static ApplicationContext applicationContext;

	/**
	 * 执行有返回值
	 *
	 * @param requiredType 类型
	 * @param action       执行方法
	 * @param names        名称
	 * @param <T1>         bean 类型
	 * @return
	 */
	public static <T1> void action(Class<T1> requiredType, Action1<T1> action, String... names) {
		action(requiredType, action, null, names);
	}

	/**
	 * 执行有返回值
	 *
	 * @param requiredType 类型
	 * @param action       执行方法
	 * @param names        名称
	 * @param <T1>         bean 类型
	 * @return
	 */
	public static <T1> void action(Class<T1> requiredType, Action1<T1> action, Action0 defaultAction, String... names) {
		T1 t1 = getBean(requiredType, names);

		if (t1 == null) {
			if (defaultAction != null) {
				defaultAction.action();
			}

			return;
		}

		action.action(t1);
	}

	/**
	 * 执行有返回值
	 *
	 * @param names 名称
	 * @return
	 */
	public static void action(Action1<Object> action, String... names) {
		action(Object.class, action, null, names);
	}

	/**
	 * 执行有返回值
	 *
	 * @param names 名称
	 * @return
	 */
	public static void action(Action1<Object> action, Action0 defaultAction, String... names) {
		action(Object.class, action, defaultAction, names);
	}

	/**
	 * 执行有返回值
	 *
	 * @param requiredType 类型
	 * @param func         执行方法
	 * @param names        名称
	 * @param <R1>         返回类型
	 * @param <T1>         bean 类型
	 * @return
	 */
	public static <R1, T1> R1 func(Class<T1> requiredType, Func1<R1, T1> func, String... names) {
		return func(requiredType, func, null, names);
	}

	/**
	 * 执行有返回值
	 *
	 * @param requiredType 类型
	 * @param func         执行方法
	 * @param names        名称
	 * @param <R1>         返回类型
	 * @param <T1>         bean 类型
	 * @return
	 */
	public static <R1, T1> R1 func(Class<T1> requiredType, Func1<R1, T1> func, Func0<R1> defaultFunc, String... names) {
		T1 t1 = getBean(requiredType, names);

		if (t1 == null) {
			if (defaultFunc == null) {
				return null;
			}

			return defaultFunc.func();
		}

		return func.func(t1);
	}

	/**
	 * 执行有返回值
	 *
	 * @param func  执行方法
	 * @param names 名称
	 * @param <R1>  返回类型
	 * @return
	 */
	public static <R1> R1 func(Func1<R1, Object> func, String... names) {
		return func(Object.class, func, null, names);
	}

	/**
	 * 执行有返回值
	 *
	 * @param func  执行方法
	 * @param names 名称
	 * @param <R1>  返回类型
	 * @return
	 */
	public static <R1> R1 func(Func1<R1, Object> func, Func0<R1> defaultFunc, String... names) {
		return func(Object.class, func, defaultFunc, names);
	}

	/**
	 * 获取 bean
	 *
	 * @param requiredType 类型
	 * @param names        名称
	 * @return bean
	 */
	public static <T> T getBean(final Class<T> requiredType, String... names) {
		return getBean(requiredType, false, names);
	}

	/**
	 * 获取 bean
	 *
	 * @param requiredType 类型
	 * @param append       附加
	 * @param names        名称
	 * @param <T>
	 * @return
	 */
	public static <T> T getBean(final Class<T> requiredType, boolean append, String... names) {
		if (applicationContext == null) {
			return null;
		}

		if (ArrayUtils.isEmpty(names)) {
			names = new String[]{requiredType.getName()};
		}

		for (String name : names) {
			if (StringUtils.isBlank(name)) {
				continue;
			}

			if (append) {
				name = requiredType.getName() + name;
			}

			if (!applicationContext.containsBean(name)) {
				continue;
			}

			return applicationContext.getBean(name, requiredType);
		}

		if (log.isErrorEnabled()) {
			for (String name : names) {
				log.error("未找到对应的bean:" + name);
			}
		}

		return null;
	}

	/**
	 * 获取 bean
	 *
	 * @param names 名称集合
	 * @return bean
	 */
	public static Object getBean(final String... names) {
		return getBean(Object.class, names);
	}

	/**
	 * 设置应用上下文
	 *
	 * @param applicationContext
	 * @throws BeansException
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		ApplicationContextHolder.applicationContext = applicationContext;
	}
}